package com.eci.context.util;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

import com.eci.common.util.JacksonUtil;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.cglib.core.Converter;

import com.eci.context.asset.annotation.ColumnMean;
import com.eci.common.asset.constant.StringConstant.Json;
import com.eci.common.asset.constant.StringConstant.Symbol;
import com.eci.common.asset.core.Page;
import com.fasterxml.jackson.databind.node.ObjectNode;

public class BeanUtil {

	private static final Map<String, BeanCopier> BEAN_COPIER_MAP = new ConcurrentHashMap<>();

	
	private static <S, T> String getKey(S source, Class<T> targetClass) {
		return source.getClass().getName() + Symbol.AT+ targetClass.getName();
	}
	
	private static <S, T> BeanCopier createBeanCopier(S source, Class<T> target, boolean useConverter) {
		return BeanCopier.create(source.getClass(), target, useConverter);
	}

	
	
	/** 使用BeanCopier拷贝对象属性 1. target不能使用链式调用
	 * {@link lombok.experimental.Accessors#chain} 2.
	 * 只会拷贝source,target属性类型和名称完全一致的字段
	 * 
	 * @param <S>  源对象泛型
	 * @param <T>  目标对象泛型
	 * @param source    源
	 * @param target    目标
	 * @param useCache  是否使用缓存
	 * @param converter converter对象
	 * @return 返回target对象
	 */
	private static <S, T> T copy(S source, T target, boolean useCache, Converter converter) {

		Objects.requireNonNull(source, "源对象不能为空");
		Objects.requireNonNull(target, "目标对象不能为空");
		boolean useConverter = Objects.nonNull(converter);

		Class<?> targetClass = target.getClass();

		String key = getKey(source, targetClass);

		BeanCopier beanCopier = getBeanCopier(useCache, key, createBeanCopier(source, targetClass, useConverter));
		beanCopier.copy(source, target, converter);
		return target;
	}

	/**  使用BeanCopier拷贝对象属性 1. target不能使用链式调用
	 * {@link lombok.experimental.Accessors#chain} 2.
	 * 只会拷贝source,target属性类型和名称完全一致的字段
	 * 
	 * @param <S>         源对象泛型
	 * @param <T>         目标对象泛型
	 * @param source      源
	 * @param targetClass 目标类对象
	 * @param useCache    是否使用缓存
	 * @param converter   converter对象

	 * @return 返回target对象
	 */
	private static <S, T> T copy(S source, Class<T> targetClass, boolean useCache, Converter converter) {

		Objects.requireNonNull(source, "源对象不能为空");
		Objects.requireNonNull(targetClass, "目标对象不能为空");
		boolean useConverter = Objects.nonNull(converter);

		String key = getKey(source, targetClass);

		BeanCopier beanCopier = getBeanCopier(useCache, key, createBeanCopier(source, targetClass, useConverter));

		T instance;
		try {
			instance = targetClass.getDeclaredConstructor().newInstance();
		} catch (InstantiationException | IllegalAccessException | NoSuchMethodException
				| InvocationTargetException e) {
			throw new IllegalArgumentException();
		}
		beanCopier.copy(source, instance, converter);
		return instance;
	}

	private static BeanCopier getBeanCopier(boolean useCache, String key, BeanCopier beanCopier2) {
		BeanCopier beanCopier;
		if (useCache) {
			beanCopier = BEAN_COPIER_MAP.computeIfAbsent(key, k -> beanCopier2);
		} else {
			beanCopier = beanCopier2;
		}
		return beanCopier;
	}
	
	public static <S, T> T copy(S source, T target) {
		return copy(source, target, true, null);
	}

	public static <S, T> T copy(S source, Class<T> targetClass) {
		return copy(source, targetClass, true, null);
	}
 
 

	/** 序列化Entity,用于保存日志记录用。 注：json的key不是字段名，而是字段上@ColumnMean entity
	 *  .必须是PageBean的子类（或者子类的子类），子类的子类的子类则返回{}
	 */
	public static String serializeEntity(Object entity) {
  
		if(entity== null)  return Json.EMPTY_OBJECT;
		
		Class<?> clazz =  entity.getClass();
		if(!Page.class.isAssignableFrom(clazz)) return Json.EMPTY_OBJECT;
 
		boolean canSerializ = false;
		int s = 0;

		// 对象的父类是PageBean
		if (Page.class == clazz.getSuperclass()) {
			canSerializ = true;
			s = 1;
		}
		// 对象的父类的父类是PageBean
		if (Page.class == clazz.getSuperclass().getSuperclass()) {
			canSerializ = true;
			s = 2;
		}
		if (!canSerializ)  return Json.EMPTY_OBJECT;

		ObjectNode node = JacksonUtil.bean2ObjNode(entity);

		// 替换字符串中有ColumnMean注解的属性为其注解内容
		Map<String, String> replaceMap = new HashMap<String, String>();

		Field[] fields = clazz.getDeclaredFields();
		if (s == 2) {
			fields = clazz.getSuperclass().getDeclaredFields();
		}

		for (Field field : fields) {
			field.setAccessible(true);
			ColumnMean cm = field.getAnnotation(ColumnMean.class);
			if (cm != null)
				replaceMap.put(field.getName(), cm.value());
		}

		for (Map.Entry<String, String> e : replaceMap.entrySet()) {

			node.set(e.getValue(), node.get(e.getKey()));
			node.remove(e.getKey());

		}
		
		return node.toString();

	}

}
